/*
 * Locate the PAT and PMT packets and print I-Frame byte offsets in
 * an H.222 transport stream (TS).
 *
 * ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is the MPEG TS, PS and ES tools.
 *
 * The Initial Developer of the Original Code is Amino Communications Ltd.
 * Portions created by the Initial Developer are Copyright (C) 2008
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *   Amino Communications Ltd, Swavesey, Cambridge UK
 *
 * ***** END LICENSE BLOCK *****
 */

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <inttypes.h>
#include <math.h>
#include <sys/stat.h>

#ifdef _WIN32
#include <stddef.h>
#else // _WIN32
#include <unistd.h>
#endif // _WIN32

#include "compat.h"
#include "ts_fns.h"
#include "misc_fns.h"
#include "pidint_fns.h"

#include "version.h"

/* NAL unit scanner */
#define EMPTY_SCANNER (0xFFFFFFFF)

/* print NAL unit to stdout */
#define NAL_INFO 0

#define TWENTY_SEVEN_MHZ  27000000

/**
 * Return 1 if stream type is video, 0 if something else
 */
extern int is_stream_type_video(unsigned s)
{
  if(s == 0x01 || s == 0x02 || s == 0x1B) // MPEG 1, 2 or 4
    return 1;
  return 0;
}

/*
 * Return PMT stream index of the video stream
 *
 * - `stream` is the stream to write to
 * - `prefix` is NULL or a string to put before each line printed
 * - `pmt` is the PMT to report on
 */
extern int parse_video_pid_from_pmt(FILE   *stream,
                       pmt_p   pmt)
{
  if (pmt == NULL)
  {
    fprintf(stream,"PMT is NULL\n");
    return -1;
  }
  if (pmt->num_streams > 0)
  {
    int ii;
    for (ii=0; ii<pmt->num_streams; ii++)
    {

      if (is_stream_type_video(pmt->streams[ii].stream_type)){
	  return ii;
      }
    }
  }
  return -2;
}
/**
 * Skip ts packet until next packet with filter pid is found.
 */
extern int skip_TS_packets(TS_reader_p  tsreader,
                              int         *payload_unit_start_indicator,
                              byte        *adapt[],
                              int         *adapt_len,
                              byte        *payload[],
                              int         *payload_len,
                              uint32_t filter_pid)
{
  printf("skipping\n");
  int err;
  uint32_t pid = 0;
  int packets_skipped = 0;
  while (pid != filter_pid) {
    err = get_next_TS_packet(tsreader, &pid, payload_unit_start_indicator,
        adapt, adapt_len, payload, payload_len);
    if (err == EOF) {
      fprintf(stderr, "\nEnd of file\n");
      return -1;
    } else if (err) {
      fprintf(stderr, "### Error reading TS packet\n");
      return -1;
    }
    packets_skipped++;
  }
  return packets_skipped;
}

/**
 * Read video PID and PCR pid and print I-Frame byte offset, length, PCR and bitrate
 */
static int parse_iframes(TS_reader_p tsreader,
			  int verbose,
			  uint32_t   video_pid,
			  uint32_t   pcrpid,
			  byte video_stream_type,
			  FILE *file,
			  int min_interval,
			  uint64_t input_file_size)
{
  int err;

  uint64_t packets_read = 0; // num of TS packets read (1 packet == 188 bytes)

  uint64_t iframe_start_packets_read = 0; // num of TS packets read at iframe start

  uint64_t last_pcr = 0;
  uint64_t current_pcr = 0;

  float current_stream_time = 0;
  uint64_t current_stream_rounded_time = 0;

  uint64_t iframestart = 0;
  uint64_t iframend = 0;


  uint64_t iframe_second = 0;

  // for bitrate calculations
  uint64_t bitrate_lastpcr_offset = 0;
  uint64_t bitrate_last_pcr = 0;

  int SeenAccessUnitDelimiter = 0;
  int nal_code = EMPTY_SCANNER;
  uint64_t last_non_idr = 0;
  uint32_t scanner = EMPTY_SCANNER;
  int last_iframe_nal_idc = -1;
  // seek to find first PCR
  // some freaky streams have first i-frame before we get the PCR
  for (;;) {
    uint32_t pid;
    int     payload_unit_start_indicator;
    byte   *adapt, *payload;
    int     adapt_len, payload_len;

    err = get_next_TS_packet(tsreader,&pid,
                             &payload_unit_start_indicator,
                             &adapt,&adapt_len,&payload,&payload_len);
    if (err == EOF) {
      if (verbose)
        fprintf(stderr, "\nEnd of file\n");
      err = EOF;
      break;
    }
    else if (err) {
      fprintf(stderr, "### Error reading TS packet\n");
      break;
    }

    if (pid == pcrpid) {

      uint64_t   adapt_pcr;
      int     got_pcr = FALSE;

      get_PCR_from_adaptation_field(adapt,adapt_len,&got_pcr,&adapt_pcr);

      if (got_pcr) {

        uint64_t first_pcr = adapt_pcr;
        current_pcr = first_pcr;
        // print first line. Print file size, first PCR in stream
        if (input_file_size) {
          fprintf(file, "-1 %"PRIu64" %"PRIu64" 0 %"PRIu64" 0\n", (input_file_size/TS_PACKET_SIZE), input_file_size, first_pcr);
        }

        last_pcr = current_pcr;
        current_pcr = adapt_pcr;
        break;
      }
    }
  }
  // start over if we are working on files
  if (tsreader->file != STDIN_FILENO)
    seek_using_TS_reader(tsreader,0);

  for (;;) {
    uint32_t pid;
    int     payload_unit_start_indicator;
    byte   *adapt, *payload;
    int     adapt_len, payload_len;

    err = get_next_TS_packet(tsreader,&pid,
                             &payload_unit_start_indicator,
                             &adapt,&adapt_len,&payload,&payload_len);
    if (err == EOF) {
      if (verbose)
        fprintf(stderr, "\nEnd of file\n");
      err = EOF;
      break;
    }
    else if (err) {
      fprintf(stderr, "### Error reading TS packet\n");
      break;
    }

    if (pid == pcrpid) {

      uint64_t   adapt_pcr;
      int     got_pcr = FALSE;

      get_PCR_from_adaptation_field(adapt,adapt_len,&got_pcr,&adapt_pcr);

      if (got_pcr) {
        last_pcr = current_pcr;
        current_pcr = adapt_pcr;
        // set first bitrate
        if( bitrate_last_pcr == 0 ) {
          bitrate_last_pcr = current_pcr;
          bitrate_lastpcr_offset = packets_read*TS_PACKET_SIZE;
        }

        if (last_pcr > current_pcr) {
          if (verbose)
            fprintf(stderr,"### PCR went back in time!\n");
          last_pcr = current_pcr;
          bitrate_last_pcr = current_pcr;
          bitrate_lastpcr_offset = packets_read*TS_PACKET_SIZE;
        }
        current_stream_time += ((current_pcr - last_pcr) / (float)TWENTY_SEVEN_MHZ);
        current_stream_rounded_time = floor(current_stream_time);
      }
    }
    if (pid == video_pid)
    {
      int i = 0;
      int scanning = 1;
      int skipped_packets = 0;
      uint64_t bitrate = 0;
      uint64_t offset = packets_read * TS_PACKET_SIZE;

      for ( i = 0 ; scanning && i < payload_len ; i++) {
        scanner <<= 8;
        scanner |= payload[i];

        switch (video_stream_type) {
        case 0x01: // MPEG 1 video
        case 0x02: // MPEG 2 video
          if (scanner == 0x00000100) { // Picture Start Code
            scanner = EMPTY_SCANNER;
            int frame_type_offset = i + 2;
            if (frame_type_offset >= TS_PACKET_SIZE) // the byte to check is in the next TS packet
            {
              skipped_packets += skip_TS_packets(tsreader,
                  &payload_unit_start_indicator, &adapt, &adapt_len, &payload,
                  &payload_len, video_pid);

              if (skipped_packets < 0) {
                // error or end of file
                fprintf(stderr, "ERROR skipping packets!\n");
                return -1;
              }
            }

            if (iframestart > 0) {
              iframend = offset;
              if (min_interval == 0 || (current_stream_rounded_time-min_interval) >= iframe_second || iframe_second == 0) {
                if (verbose)
                  printf(
                      "Found MPEG-2 I-Frame at %"PRIu64"(=%f) (%"PRIu64" - %"PRIu64", size %"PRIu64" bytes) \n",
                      current_stream_rounded_time, current_stream_time, iframestart, iframend,
                      (iframend - iframestart));

                iframe_second = current_stream_rounded_time+min_interval;
                // write offsets to file
                bitrate = 8*TWENTY_SEVEN_MHZ * (offset-bitrate_lastpcr_offset) / (current_pcr-bitrate_last_pcr);
                fprintf(file, "%"PRIu64" %"PRIu64" %"PRIu64" %"PRIu64" %"PRIu64" %"PRIu64"\n",
                    current_stream_rounded_time, iframe_start_packets_read, iframestart,
                    (iframend - iframestart), current_pcr, bitrate);
                bitrate_last_pcr = current_pcr;
                bitrate_lastpcr_offset = offset;

              }
              iframestart = 0;
              iframend = 0;
              // end of frame
            }

            int frame_type = (payload[frame_type_offset] >> 3) & 0x07;
            if (frame_type == 1) { // I-Frame
              iframestart = offset;
              iframe_start_packets_read = packets_read;
              iframend = 0;
              scanning = 0; // we can stop scanning this packet as we have already found an i-frame
            }
          }
          break;
        case 0x1B: // MPEG 4 video

          nal_code = scanner >> 8;
          int zero_bit = (payload[i] >> 7);
          if (nal_code == 0x0000001 && zero_bit == 0) { // got nal unit
          int nal_unit_type = payload[i] & 0x1F;
          int nal_ref_idc = (payload[i] >> 5) & 0x03;
          // NAL analysis
          if (verbose)
            printf("packet: %"PRIu64" \t nal_code: 0x%08x \t nal_ref_idc: 0x%02x \t nal_unit_type: 0x%02x (%d)\n", packets_read, nal_code, nal_ref_idc, nal_unit_type,nal_unit_type);


          if (nal_unit_type == 0x09) { // Access Unit Delimiter
             scanner = EMPTY_SCANNER;
             SeenAccessUnitDelimiter = 1;
          }
          else if (SeenAccessUnitDelimiter && nal_unit_type == 0x01)
          {
            // if i-frame was found
            if (iframestart > 0) {
              // so here it gets tricky.
              // We are looking for nal_ref_idc not equal to zero, but sometimes this just doesn't happen.
              // Then we check if nal_ref_idc changes and assume that the i-frame has ended.
              if (nal_ref_idc == 0 || nal_ref_idc != last_iframe_nal_idc) {
                last_iframe_nal_idc = -1;
                if ( last_non_idr == 0 ) iframend = offset; // if no non-idr frames were in between
                else iframend = last_non_idr;

                // to print out or not
                if ((current_stream_rounded_time) >= iframe_second || min_interval == 0 || iframe_second == 0) {
                  if (verbose) {
                   printf(
                       "Found H264 I-Frame at %"PRIu64"(%f) (%"PRIu64" - %"PRIu64", size %"PRIu64" bytes) \n",
                       current_stream_rounded_time, current_stream_time, iframestart, iframend,
                       (iframend - iframestart));
                  }
                  iframe_second = current_stream_rounded_time+min_interval;
                  // write offsets to file, [time packet_num, byte_offset_start, byte_offset_end, pcr_value, bitrate ]
                  if ( (current_pcr-bitrate_last_pcr) > 0)
                    bitrate = 8 * TWENTY_SEVEN_MHZ * (offset-bitrate_lastpcr_offset) / (current_pcr-bitrate_last_pcr);
                  else bitrate = 0;
                  fprintf(file, "%"PRIu64" %"PRIu64" %"PRIu64" %"PRIu64" %"PRIu64" %"PRIu64"\n",
                     current_stream_rounded_time, iframe_start_packets_read, iframestart,
                     (iframend - iframestart), current_pcr, bitrate);
                  bitrate_last_pcr = current_pcr;
                  bitrate_lastpcr_offset = offset;
                }
                // write offsets to file
                iframestart = 0;
                iframend = 0;
             }
             // remember last offset of non-idr part of an iframe
             last_non_idr = offset;
            }
          }
          else if (SeenAccessUnitDelimiter && nal_unit_type == 0x07) { // NALU start
             SeenAccessUnitDelimiter = 0;

             // if two i-frames in a row
             if (iframestart > 0) {
               iframend = offset;
               if ((current_stream_rounded_time) >= iframe_second || min_interval == 0 || iframe_second == 0) {
                 if (verbose) {
                   printf(
                       "Found H264 I-Frame at %"PRIu64"(%f) (%"PRIu64" - %"PRIu64", size %"PRIu64" bytes) \n",
                       current_stream_rounded_time, current_stream_time, iframestart, iframend,
                       (iframend - iframestart));
                 }
                 iframe_second = current_stream_rounded_time+min_interval;
                 // write offsets to file, [time packet_num, byte_offset_start, byte_offset_end, pcr_value, bitrate ]
                 if ( (current_pcr-bitrate_last_pcr) > 0)
                   bitrate = 8 * TWENTY_SEVEN_MHZ * (offset-bitrate_lastpcr_offset) / (current_pcr-bitrate_last_pcr);
                 else bitrate = 0;
                 fprintf(file, "%"PRIu64" %"PRIu64" %"PRIu64" %"PRIu64" %"PRIu64" %"PRIu64"\n",
                     current_stream_rounded_time, iframe_start_packets_read, iframestart,
                     (iframend - iframestart), current_pcr, bitrate);
                 bitrate_last_pcr = current_pcr;
                 bitrate_lastpcr_offset = offset;

               }
               // write offsets to file
               iframestart = 0;
               iframend = 0;
             }
             if ( verbose )
               printf("Start of Iframe, nal_unit = %d\n",nal_unit_type);
             iframestart = offset;
             iframe_start_packets_read = packets_read;
             iframend = 0;
             last_non_idr = 0;
             scanning = 0; // skip to next TS Packet
             scanner = EMPTY_SCANNER;
             last_iframe_nal_idc = nal_ref_idc;
            }
          }
		break;
        default:
          break;
        }
      }
      packets_read += skipped_packets;
    }

    packets_read++;

  }
  if (verbose)
    printf(" %ld TS packets read (%ld bytes), duration %f seconds\n", packets_read,(packets_read*TS_PACKET_SIZE),current_stream_time);

  return err;
}

/*
 * Report on the program streams, by looking at the PAT and PMT packets
 * in the first `max` TS packets of the given input stream
 *
 * Returns 0 if all went well, 1 if something went wrong.
 */
static int parse_streams(TS_reader_p tsreader,
                          int         max,
                          int         verbose,
                          uint32_t   *video_pid,
                          uint32_t   *pcr_pid,
                          byte   *video_type)
{
  int     err;
  int     ii;

  pidint_list_p  this_prog_list = NULL;
  pidint_list_p  last_prog_list = NULL;
  pmt_p          this_pmt = NULL;
  pmt_p          last_pmt = NULL;

  uint32_t       pmt_pid = 0;  // which will get "masked" by the PAT pid

  byte  *pat_data = NULL;
  int    pat_data_len = 0;
  int    pat_data_used = 0;

  byte  *pmt_data = NULL;
  int    pmt_data_len = 0;
  int    pmt_data_used = 0;

  int num_pats = 0;
  int num_pmts = 0;
  int vindex   = -1;

  if(verbose)
    printf("Scanning %d TS packets\n",max);

  for (ii=0; ii<max; ii++)
  {
    uint32_t pid;
    int      payload_unit_start_indicator;
    byte    *adapt, *payload;
    int      adapt_len, payload_len;

    err = get_next_TS_packet(tsreader,&pid,
                             &payload_unit_start_indicator,
                             &adapt,&adapt_len,&payload,&payload_len);
    if (err == EOF)
    {
      printf("EOF\n");
      break;
    }
    else if (err)
    {
      fprintf(stderr,"### Error reading TS packet %d\n",ii+1);
      if (pat_data) free(pat_data);
      free_pidint_list(&last_prog_list);
      free_pmt(&last_pmt);
      if (pmt_data) free(pmt_data);
      return 1;
    }

    if (pid == 0x0000)
    {
      num_pats++;
      if (verbose)
        printf("Packet %d is PAT\n",ii+1);
      if (payload_len == 0)
      {
        printf("Packet %d is PAT, but has no payload\n",ii+1);
        continue;
      }

      if (payload_unit_start_indicator && pat_data)
      {
        // This is the start of a new PAT packet, but we'd already
        // started one, so throw its data away
        fprintf(stderr,"!!! Discarding previous (uncompleted) PAT data\n");
        free(pat_data);
        pat_data = NULL; pat_data_len = 0; pat_data_used = 0;
      }
      else if (!payload_unit_start_indicator && !pat_data)
      {
        // This is the continuation of a PAT packet, but we hadn't
        // started one yet
        fprintf(stderr,"!!! Discarding PAT continuation, no PAT started\n");
        continue;
      }

      err = build_psi_data(verbose,payload,payload_len,pid,
                           &pat_data,&pat_data_len,&pat_data_used);
      if (err)
      {
        fprintf(stderr,"### Error %s PAT\n",
                (payload_unit_start_indicator?"starting new":"continuing"));
        if (pat_data) free(pat_data);
        free_pidint_list(&last_prog_list);
        free_pmt(&last_pmt);
        if (pmt_data) free(pmt_data);
        return 1;
      }

      // Do we need more data to complete this PAT?
      if (pat_data_len > pat_data_used)
        continue;

      err = extract_prog_list_from_pat(verbose,pat_data,pat_data_len,
                                       &this_prog_list);
      if (err)
      {
        free_pidint_list(&last_prog_list);
        free_pmt(&last_pmt);
        if (pmt_data) free(pmt_data);
        free(pat_data);
        return err;
      }

      free(pat_data);
      pat_data = NULL; pat_data_len = 0; pat_data_used = 0;
      num_pats++;

      if (err)
      {
        free_pidint_list(&last_prog_list);
        free_pmt(&last_pmt);
        if (pmt_data) free(pmt_data);
        return err;
      }

      if (!same_pidint_list(this_prog_list,last_prog_list))
      {
        if (last_prog_list != NULL)
          printf("\nPacket %d is PAT - content changed\n",ii+1);

        if (this_prog_list->length == 0)
          printf("No programs defined in PAT (packet %d)\n",ii+1);
        else
        {
          if (this_prog_list->length > 1)
            printf("Multiple programs in PAT - using the first\n");
          pmt_pid = this_prog_list->pid[0];
        }
      }
      free_pidint_list(&last_prog_list);
      last_prog_list = this_prog_list;
    }
    else if (pid == pmt_pid)
    {
      if (verbose)
        printf("Packet %d is PMT with PID %04x (%d)%s\n",ii+1,pid,pid,
               (payload_unit_start_indicator?"[pusi]":""));
      if (payload_len == 0)
      {
        printf("Packet %d is PMT, but has no payload\n",ii+1);
        continue;
      }

      if (payload_unit_start_indicator && pmt_data)
      {
        // This is the start of a new PMT packet, but we'd already
        // started one, so throw its data away
        fprintf(stderr,"!!! Discarding previous (uncompleted) PMT data\n");
        free(pmt_data);
        pmt_data = NULL; pmt_data_len = 0; pmt_data_used = 0;
      }
      else if (!payload_unit_start_indicator && !pmt_data)
      {
        // This is the continuation of a PMT packet, but we hadn't
        // started one yet
        fprintf(stderr,"!!! Discarding PMT continuation, no PMT started\n");
        continue;
      }

      err = build_psi_data(verbose,payload,payload_len,pid,
                           &pmt_data,&pmt_data_len,&pmt_data_used);
      if (err)
      {
        fprintf(stderr,"### Error %s PMT\n",
                (payload_unit_start_indicator?"starting new":"continuing"));
        free_pidint_list(&this_prog_list);
        free_pmt(&last_pmt);
        if (pmt_data) free(pmt_data);
        return 1;
      }

      // Do we need more data to complete this PMT?
      if (pmt_data_len > pmt_data_used)
        continue;

      err = extract_pmt(verbose,pmt_data,pmt_data_len,pid,&this_pmt);
      if (err)
      {
        free_pidint_list(&this_prog_list);
        free_pmt(&last_pmt);
        free_pmt(&this_pmt);
        if (pmt_data) free(pmt_data);
        return err;
      }

      free(pmt_data);
      pmt_data = NULL; pmt_data_len = 0; pmt_data_used = 0;
      num_pmts++;

      if (same_pmt(this_pmt,last_pmt))  // Nothing to do
      {
        free_pmt(&this_pmt);
        continue;
      }

      if (last_pmt != NULL)
        printf("\nPacket %d is PMT with PID %04x (%d)"
               " - content changed\n",ii+1,pid,pid);

      vindex = parse_video_pid_from_pmt(stdout,this_pmt);
      if (vindex >= 0) {
        *video_type = this_pmt->streams[vindex].stream_type;
        *video_pid = this_pmt->streams[vindex].elementary_PID;
        *pcr_pid = this_pmt->PCR_pid;
        break;
      } else {
        printf("\nERROR: no video pid found in PMT!\n");
      }

      free_pmt(&last_pmt);
      last_pmt = this_pmt;
    }
  }
  if  (verbose)
    printf("\nScanned %d packets to get PAT/PMT\n", ii+1 );
  free_pidint_list(&last_prog_list);
  free_pmt(&last_pmt);
  if (pmt_data) free(pmt_data);
  return 0;
}

static void print_usage()
{
  printf(
    "Usage: tsiframeindex [switches] [<infile.ts>] [<outfile.idx>]\n"
    "\n"
    );
  REPORT_VERSION("tsiframeindex");
  printf(
    "\n"
    "  Print I-Frame start time from the beginning of the Transport Stream \n"
    "  and print byte offsets of the I-Frames.\n"
    "\n"
    "Files:\n"
    "  <infile>  is an H.222 Transport Stream file (but see -stdin). \n"
    "  <outfile> output file name. Print to sysout if not set.\n"
    "        format: [time packet_num byte_offset_start iframe_length_bytes pcr_value bitrate]"
    ""
    "\n"
    "\n"
    "Switches:\n"
    "  -stdin             Input from standard input, instead of a file. Print output to sysout.\n"
    "  -verbose, -v       Output extra information about packets\n"
    "  -max <n>, -m <n>   Number of TS packets to scan to find PAT/PMT. Defaults to 2000.\n"
    "  -interval <n>, -i <n>   Print only I-frames that are <n> seconds apart.\n"
    "  -nice <n>, -n <n>  Set process priority (default: 0)\n"
    );
}

int main(int argc, char **argv)
{
  int    use_stdin      = FALSE;
  int    had_input_name = FALSE;
  int    max            = 10000;
  int    min_interval   = 0;
  int    verbose        = FALSE; // True => output diagnostic/progress messages
  int    err = 0;
  int    nice_prio      = 0;
  char   *input_name    = NULL;
  char   *output_name   = NULL;
  uint32_t    video_pid       = 0;
  uint32_t    pcr_pid         = 0;
  uint64_t    input_file_size = 0;
  byte        video_type      = 0;

  TS_reader_p  tsreader    = NULL;
  FILE        *output_file = NULL;
  struct stat stat_buf;

  int    ii = 1;

  if (argc < 2)
  {
    print_usage();
    return 0;
  }

  while (ii < argc)
  {
    if (argv[ii][0] == '-')
    {
      if (!strcmp("--help",argv[ii]) || !strcmp("-h",argv[ii]) ||
          !strcmp("-help",argv[ii]))
      {
        print_usage();
        return 0;
      }
      else if (!strcmp("-verbose",argv[ii]) || !strcmp("-v",argv[ii]))
      {
        verbose = TRUE;
      }
      else if (!strcmp("-max",argv[ii]) || !strcmp("-m",argv[ii]))
      {
        CHECKARG("tsiframeindex",ii);
        err = int_value("tsiframeindex",argv[ii],argv[ii+1],TRUE,10,&max);
        if (err) return 1;
        ii++;
      }
      else if (!strcmp("-nice",argv[ii]) || !strcmp("-n",argv[ii]))
      {
        CHECKARG("tsiframeindex",ii);
        err = int_value("tsiframeindex",argv[ii],argv[ii+1],TRUE,10,&nice_prio);
        if (err) return 1;
        if (nice_prio < -19)
        {
            nice_prio = -19;
        }
        else if (nice_prio > 19)
        {
            nice_prio = 19;
        }
        ii++;
      }
      else if (!strcmp("-interval",argv[ii]) || !strcmp("-i",argv[ii]))
      {
        CHECKARG("tsiframeindex",ii);
        err = int_value("tsiframeindex",argv[ii],argv[ii+1],TRUE,10,&min_interval);
        if ( min_interval < 0 ) {
          fprintf(stderr,"### tsiframeindex: "
                  "Min interval can't be negative!\n");

          return 1;
        }
        if (err) return 1;
        ii++;
      }
      else if (!strcmp("-stdin",argv[ii]))
      {
        use_stdin = TRUE;
        had_input_name = TRUE;  // so to speak
        output_file = stdout;

      }
      else
      {
        fprintf(stderr,"### tsiframeindex: "
                "Unrecognised command line switch '%s'\n",argv[ii]);
        return 1;
      }
    }
    else
    {
      if (had_input_name && output_name )
      {
        fprintf(stderr,"### tsiframeindex: Unexpected '%s'\n",argv[ii]);
        return 1;
      }
      else
      {
        if ( !input_name ) {
	      input_name = argv[ii];
	      had_input_name = TRUE;
	    }
	    else {
          output_name = argv[ii];
	    }
      }
    }
    ii++;
  }

  if (!had_input_name)
  {
    fprintf(stderr,"### tsiframeindex: No input file specified\n");
    return 1;
  }

  if (output_name)
  {
    output_file = fopen(output_name,"w");
    if (!output_file) {
      fprintf(stderr,"### tsiframeindex: Unable to open file %s\n", output_name);
      return 1;
    }
  } else if (!output_file){
	  output_file = stdout;
  }




  err = open_file_for_TS_read((use_stdin?NULL:input_name),&tsreader);
  if (err)
  {
    fprintf(stderr,"### tsiframeindex: Unable to open input file %s for reading TS\n",
            use_stdin?"<stdin>":input_name);
    return 1;
  }
  if (verbose)
    printf("Reading from %s\n",(use_stdin?"<stdin>":input_name));

  if (!use_stdin) {
    int fd = tsreader->file;
    if ( (fd == -1) || (fstat(fd, &stat_buf) != 0))
    {
        fprintf(stderr,"### tsiframeindex: cannot open input file (%s)\n", input_name);
	return 1;
    }
    input_file_size = stat_buf.st_size;
  }

  /* set process priority */
  if (nice_prio)
  {
    int prio = nice(nice_prio);
    if (prio < 0)
    {
      fprintf(stderr,"cannot set process priority (%d)\n", nice_prio);
      if ( output_file != stdout ) fclose(output_file);
      err = close_TS_reader(&tsreader);
      return (EXIT_FAILURE);
    }
    if (verbose)
      printf("process priority: %d\n", prio);
  }


  err = parse_streams(tsreader,max,verbose, &video_pid, &pcr_pid, &video_type);
  if (err)
  {
    fprintf(stderr,"### tsiframeindex: Error reporting on stream\n");
    (void) close_TS_reader(&tsreader);
    return 1;
  }

  if (!use_stdin) {
	  // start over
	  seek_using_TS_reader(tsreader,0);
  }
  err = parse_iframes(tsreader, verbose, video_pid, pcr_pid, video_type, output_file, min_interval, input_file_size);

  if ( output_file != stdout ) fclose(output_file);
  err = close_TS_reader(&tsreader);
  if (err) return 1;

  return 0;
}

// Local Variables:
// tab-width: 8
// indent-tabs-mode: nil
// c-basic-offset: 2
// End:
// vim: set tabstop=8 shiftwidth=2 expandtab:
